﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Profile;
using System.Web.Security;
using Medianamik.Core.Interfaces;
using Medianamik.Core.Logging;

namespace Medianamik.Core.Profile
{
    public static class ProfileNodeHelper
    {
        private static ILogger Logger = new ObjectFactory<ILogger>().Get("MedianamikProfileProviderLogger");

        //public static ProfileNode SerializeProfileNodeProperties(this ProfileNode profile)
        //{
        //    return profile == null ? null : profile.SerializeProfileNodeProperties(GetConfiguredBaseProfileClassProperties());
        //}
        //public static ProfileNode SerializeProfileNodeProperties(this ProfileNode profile, SettingsPropertyCollection settings)
        //{
        //    if (profile == null)
        //        return null;

        //    Serialize(profile, settings);

        //    return profile;
        //}

        public static ProfileNode DeserializeProfileNodeProperties(this ProfileNode profile)
        {
            return profile == null ? null : profile.DeserializeProfileNodeProperties(GetConfiguredBaseProfileClassProperties());
        }
        internal static ProfileNode DeserializeProfileNodeProperties(this ProfileNode profile, SettingsPropertyCollection settings)
        {
            if (profile == null)
                return null;

            var currentStatus = profile.Status;

            foreach (SettingsProperty property in settings)
            {
                if (!property.Name.Equals("User") &&
                    !property.Name.Equals("NodeId") &&
                    property.PropertyType != typeof(IEnumerable<Guid>) && !
                                                                          property.PropertyType.GetInterfaces().Any(x =>
                                                                                                                    x.IsGenericType &&
                                                                                                                    x.GetGenericTypeDefinition() == typeof(IEnumerable<Guid>)))
                {
                    if (property.SerializeAs == SettingsSerializeAs.ProviderSpecific)
                        if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
                            property.SerializeAs = SettingsSerializeAs.String;
                        else
                            property.SerializeAs = SettingsSerializeAs.Xml;

                    var value = new SettingsPropertyValue(property);

                    if (!profile.DefaultInstance.Properties.Exists(value.Name))
                    {
                        Logger.Warn("Profile Property '" + value.Name + "' does not exists on ProfileNode");
                        continue;
                    }

                    value.SerializedValue = profile.DefaultInstance.Properties[value.Name].Value;
                    profile.DefaultInstance.Properties[value.Name]
                        .SetValue(value.PropertyValue ?? value.SerializedValue);
                }
            }

            profile.Status = currentStatus;

            return profile;
        }

        //internal static void SerializeNodeProperties(this Node profile)
        //{
        //    if (profile == null)
        //        return;

        //    if (!profile.NodeTypeID.Equals(MedianamikTypes.MedianamikProfile))
        //        throw new Exception("Cannot deserialize node of other type than MedianamikProfile");

        //    Serialize(profile, GetConfiguredBaseProfileClassProperties());
        //}

        //private static void Serialize(INode profile, SettingsPropertyCollection settings)
        //{
        //    // Iterate through the settings to be retrieved
        //    foreach (SettingsProperty property in settings)
        //    {
        //        if (!property.Name.Equals("User") &&
        //            !property.Name.Equals("NodeId") &&
        //            property.PropertyType != typeof(IEnumerable<Guid>) && !property.PropertyType.GetInterfaces().Any(x =>
        //                x.IsGenericType &&
        //                x.GetGenericTypeDefinition() == typeof(IEnumerable<Guid>)))
        //        {
        //            if (property.SerializeAs == SettingsSerializeAs.ProviderSpecific)
        //                if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
        //                    property.SerializeAs = SettingsSerializeAs.String;
        //                else
        //                    property.SerializeAs = SettingsSerializeAs.Xml;

        //            var value = new SettingsPropertyValue(property);

        //            if (!profile.DefaultInstance.Properties.Exists(value.Name))
        //            {
        //                Logger.Warn("Profile Property '" + value.Name + "' does not exists on ProfileNode");
        //                continue;
        //            }

        //            //if (value.IsDirty)
        //            //{
        //            value.PropertyValue = profile.DefaultInstance.Properties[value.Name].GetValue(property.PropertyType);
        //            profile.DefaultInstance.Properties[value.Name].SetValue(value.SerializedValue);
        //            //}
        //        }
        //    }
        //}

        public static SettingsPropertyCollection GetConfiguredBaseProfileClassProperties()
        {
            var profileSection =
                (ProfileSection)
                ConfigurationManager.GetSection("system.web/profile");
            var inherits = profileSection.Inherits;

            if (String.IsNullOrEmpty(inherits))
                inherits = "Medianamik.Core.Profile.UserProfile";

            var fAnonEnabled = HostingEnvironment.IsHosted ? AnonymousIdentificationModule.Enabled : true;
            var inheritsFromType = GetGlobalType(inherits) ?? TryLoadingFromDLLsInBinFolder(inherits);

            var settingProperties = new SettingsPropertyCollection();

            if (inheritsFromType != typeof(ProfileBase))
            {
                var properties = typeof(ProfileBase).GetProperties();
                var values = new NameValueCollection(properties.Length);

                foreach (var info in properties)
                {
                    values.Add(info.Name, string.Empty);
                }

                foreach (var info2 in inheritsFromType.GetProperties())
                {
                    if (values[info2.Name] == null)
                    {
                        var provider = ProfileManager.Provider;
                        var isReadOnly = false;
                        var providerSpecific = SettingsSerializeAs.ProviderSpecific;
                        var defaultValue = string.Empty;
                        var allow = false;
                        string customProviderData = null;

                        foreach (var attribute in Attribute.GetCustomAttributes(info2, true))
                        {
                            if (attribute is SettingsSerializeAsAttribute)
                            {
                                providerSpecific = ((SettingsSerializeAsAttribute)attribute).SerializeAs;
                            }
                            else if (attribute is SettingsAllowAnonymousAttribute)
                            {
                                allow = ((SettingsAllowAnonymousAttribute)attribute).Allow;
                                if (!fAnonEnabled && allow)
                                {
                                    throw new ConfigurationErrorsException("hummm...");
                                }
                            }
                            else if (attribute is ReadOnlyAttribute)
                            {
                                isReadOnly = ((ReadOnlyAttribute)attribute).IsReadOnly;
                            }
                            else if (attribute is DefaultSettingValueAttribute)
                            {
                                defaultValue = ((DefaultSettingValueAttribute)attribute).Value;
                            }
                            else if (attribute is CustomProviderDataAttribute)
                            {
                                customProviderData = ((CustomProviderDataAttribute)attribute).CustomProviderData;
                            }
                        }

                        var attributes = new SettingsAttributeDictionary();
                        attributes.Add("AllowAnonymous", allow);

                        if (!string.IsNullOrEmpty(customProviderData))
                        {
                            attributes.Add("CustomProviderData", customProviderData);
                        }

                        var property = new SettingsProperty(info2.Name, info2.PropertyType, provider, isReadOnly, defaultValue, providerSpecific, attributes, false, true);
                        settingProperties.Add(property);
                    }
                }
            }

            return settingProperties;
        }

        private static Type TryLoadingFromDLLsInBinFolder(string  className)
        {
            var binFolderPath = Path.GetDirectoryName(new Uri((Assembly
                .GetExecutingAssembly()).CodeBase).LocalPath);

            var fileEntries = Directory.GetFiles(binFolderPath);

            foreach (var fileName in fileEntries)
            {
                if(Path.GetExtension(fileName).Equals(".dll", StringComparison.OrdinalIgnoreCase))
                {
                    try
                    {
                        Assembly.LoadFile(fileName);
                    }
                    catch{}
                }

                var type = GetGlobalType(className);

                if (type != null) return type;
            }

            return null;
        }

        public static Type GetGlobalType(string s)
        {
            Type t = null;
            Assembly[] av = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly a in av)
            {
                t = Type.GetType(s + "," + a.GetName());
                if (t != null)
                    break;
            }
            return t;
        }
    }
}
